import datetime
import functools
import inspect
import sys
import time
import traceback

from re_common.baselibrary.utils.basetime import BaseTime


def format_t(frame_dict):
    row = []
    row.append('  File "{}", line {}, in {},index {}\n'.format(
        frame_dict[0], frame_dict[1], frame_dict[2], frame_dict[4]))
    if frame_dict[3]:
        row.append('    {}\n'.format(frame_dict[3][0].strip()))
    return ''.join(row)


def func_time(callback=None, is_print=False):
    """
    装饰器获取时间
    :param func:
    :return:
    """

    def dewrapper(func):
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            list_exception_name = []
            try:
                list_exception_name = kwargs.get("list_exception_name", [])
                if "list_exception_name" in list(kwargs.keys()):
                    del kwargs["list_exception_name"]
                start = time.time()
                result = await func(*args, **kwargs)
                end = time.time()
                if is_print:
                    print(func.__name__, end - start)
                if callback is not None:
                    await callback(start, end, func.__name__, *args, **kwargs)
                return result
            except Exception:
                if sys.exc_info()[0].__name__ in list_exception_name:
                    raise sys.exc_info()[0]
                else:
                    from app_main import request_ctx_var
                    from app_main import app
                    from apps.sql_app.mmongodb import Coll
                    request = request_ctx_var.get()
                    m = dict()
                    if request:
                        m["client_ip"] = str(request.client.host)
                        m["request_url"] = str(request.url)
                        m["request_body"] = str(request.state.mbody)
                        m["client_ip"] = str(request.client.host)
                    m["ip_port"] = app.state.ip + ":" + str(app.state.port)
                    m["write_time"] = str(datetime.datetime.now())
                    m["func_name"] = func.__name__
                    m["traceback_str"] = traceback.format_exc()
                    m["args"] = str(args)
                    m["kwargs"] = str(kwargs)
                    m["err_source"] = "server"
                    m["stack"] = ''.join([format_t(inspect.getframeinfo(i.frame)) for i in inspect.stack()[:10]])
                    mongonn = Coll.get_post_user_err()
                    # await mongonn.insert_one(m)
                    from apps.crawler_platform.util.nosqlhelper import NOSQLHelper
                    result = await NOSQLHelper.insert_one(mongonn, m,
                                                          list_exception_name=["DocumentTooLarge"])
                    raise Exception("sql or nosql 执行错误" + traceback.format_exc())

        return wrapper

    return dewrapper


async def callback(start, end, func_name, *args, **kwargs):
    from apps.sql_app.mmongodb import Coll
    time_run = end - start
    if time_run > 5:
        jsons = {
            "time_run": time_run,
            "func_name": func_name,
            "args": str(args),
            "kwargs": str(kwargs),
            "create_time": BaseTime().get_beijin_date_strins()
        }
        mongonn = Coll.get_func_time()
        await mongonn.insert_one(jsons)


class SQLHelper(object):

    def __init__(self):
        pass

    @classmethod
    @func_time(callback)
    async def fetchone(self, sql, args=None, *cursor_args):
        """
        查询单条语句
        :param sql:
        :param args:
        :param cursor_args:
        :return:
        """
        from main import app
        pool = app.state.pool
        async with pool.acquire() as conn:
            async with conn.cursor(*cursor_args) as cur:
                await cur.execute(sql, args)
                row = await cur.fetchone()
        return row

    @classmethod
    @func_time(callback)
    async def fetchall(self, sql, args=None, *cursor_args):
        """
        查询多条语句
        :param sql:
        :param args:
        :param cursor_args:
        :return:
        """
        from main import app
        pool = app.state.pool
        async with pool.acquire() as conn:
            async with conn.cursor(*cursor_args) as cur:
                await cur.execute(sql, args)
                row = await cur.fetchall()
        return row

    @classmethod
    @func_time(callback)
    async def execute_commit(self, sql, args=None, *cursor_args):
        """
        执行提交
        请研究返回
        :param sql:
        :param args:
        :param cursor_args:
        :return:
        """
        from main import app
        pool = app.state.pool
        async with pool.acquire() as conn:
            async with conn.cursor(*cursor_args) as cur:
                rows = await cur.execute(sql, args)
                await conn.commit()
        return rows

    @classmethod
    @func_time(callback)
    async def executemany_commit(self, sql, args=None, *cursor_args):
        """
        执行提交
        请研究返回
        :param sql:
        :param args:
        :param cursor_args:
        :return:
        """
        from main import app
        pool = app.state.pool
        async with pool.acquire() as conn:
            async with conn.cursor(*cursor_args) as cur:
                await cur.executemany(sql, args)
                await conn.commit()
        return True

    @classmethod
    @func_time(callback)
    async def executemany_list_commit(self, sqls_list, args=None, *cursor_args):
        """
        执行提交
        请研究返回
        sqls_list 内部 为一个(sql, data) 的列表
        :param sql:
        :param args:
        :param cursor_args:
        :return:
        """
        row_list = []
        from main import app
        pool = app.state.pool
        async with pool.acquire() as conn:
            async with conn.cursor(*cursor_args) as cur:
                for sql in sqls_list:
                    rows = await cur.execute(sql[0], sql[1])
                    row_list.append(rows)
                await conn.commit()
        return row_list
